package com.uinnova.product.eam.init.cmdb;

import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.json.simple.JSONObject;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.net.URI;
import java.security.MessageDigest;
import java.util.*;

/**
 * 用于模拟HTTP请求中GET/POST/PUT/DELETE方式 v2
 * author : July
 */

public class EasyOps2 {

    private Map<String, String> headers;
    private String host;
    private String ACCESS_KEY;
    private String SECRET_KEY;

    public static void main(String[] args) {
        EasyOps2 easyops = new EasyOps2("10.0.241.159", "99915e6f1658ad5888ec0396", "51654b754c6f56645761717155784e66415450574d50625656694d5477654374");

//        System.out.println("******************ADD******************");
//        Map<String, Object> map1 = new HashMap<String, Object>();
//        map1.put("name", "策略17");
//        String ret1 = easyops.sendRequest("/cmdb/object/DEL/instance", map1, "POST");
//        System.out.println(ret1);
//        System.out.println("******************END******************\n");

//        System.out.println("******************DELETE******************");
//        Map<String, Object> map2 = new HashMap<String, Object>();
//        map2.put("relation_id", "DEL_HOST_DEL_HOST");
//        map2.put("left_instance_id", "5c6a5d543edd9");
//        map2.put("right_instance_id", "5b86d928d0628");
//        String del_ret = easyops.sendRequest("/cmdb_resource/object_relation/DEL_HOST_DEL_HOST/relation_instance", map2, "DELETE");
//        System.out.println(del_ret);
//        System.out.println("******************END******************\n");

//        System.out.println("******************MODIFY******************");
//        Map<String, Object> map3 = new HashMap<String, Object>();
//        map3.put("name", "策略18");
//        String ret3 = easyops.sendRequest("/cmdb/object/DEL/instance/5c6a5d543edd9", map3, "PUT");
//        System.out.println(ret3);
//        System.out.println("******************END******************\n");

//        System.out.println("******************SEARCH******************");
//        List<Object> ret4 = easyops.sendPostWithAll("DEL", null);
//        System.out.println(ret4);
//        System.out.println("******************END******************\n");

        System.out.println("******************GET&中文参数******************");
        Map<String, Object> map5 = new HashMap<String, Object>();
        map5.put("hostname", "测试");
        String ret5 = easyops.sendRequest("/cmdb/device/search", map5, "GET");
        System.out.println(ret5);
        System.out.println("******************END******************\n");

    }

    /**
     * 初始化
     *
     * @param host       服务器IP
     * @param access_key ACCESS——KEY
     * @param secret_key SECRET-KEY
     */
    public EasyOps2(String host, String access_key, String secret_key) {
        Map<String, String> hd = new TreeMap<String, String>();
        hd.put("Host", "openapi.easyops-only.com");
        hd.put("Content-Type", "application/json");
        this.headers = hd;
        this.host = host;
        this.ACCESS_KEY = access_key;
        this.SECRET_KEY = secret_key;
    }

    /***
     * GET请求
     * @param url 请求URL
     * @param param 请求参数
     * @return String 请求返回200
     */
    private String doGet(String url, Map<String, String> param) {

        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();

        String resultString = "";
        CloseableHttpResponse response = null;
        try {
            // 创建uri
            URIBuilder builder = new URIBuilder(url);
            if (param != null) {
                for (String key : param.keySet()) {
                    builder.addParameter(key, param.get(key));
                }
            }
            URI uri = builder.build();

            // 创建http GET请求
            HttpGet httpGet = new HttpGet(uri);
            System.out.println(uri);

            // 设置通用属性
            httpGet.setHeader("Accept", "*/*");
            httpGet.setHeader("Connection", "Keep-Alive");
            httpGet.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
            for (Object hd : this.headers.keySet()) {
                httpGet.setHeader(hd.toString(), this.headers.get(hd.toString()));
            }

            // 执行请求
            response = httpClient.execute(httpGet);
            // 判断返回状态是否为200
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
//            System.out.println(resultString);
//            if (response.getStatusLine().getStatusCode() == 200) {
//                resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
//            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultString;
    }

    /**
     * DELETE 请求
     *
     * @param url   请求URL
     * @param param 请求参数
     * @return String 请求返回200
     */
    private String doDel(String url, Map<String, String> param) {

        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();

        String resultString = "";
        CloseableHttpResponse response = null;
        try {
            // 创建uri
            URIBuilder builder = new URIBuilder(url);
            if (param != null) {
                for (String key : param.keySet()) {
                    builder.addParameter(key, param.get(key));
                }
            }
            URI uri = builder.build();

            // 创建http DELETE请求
            HttpDelete httpDelete = new HttpDelete(uri);
            System.out.println(uri);

            // 设置通用属性
            httpDelete.setHeader("Accept", "*/*");
            httpDelete.setHeader("Connection", "Keep-Alive");
            httpDelete.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
            for (Object hd : this.headers.keySet()) {
                httpDelete.setHeader(hd.toString(), this.headers.get(hd.toString()));
            }

            // 执行请求
            response = httpClient.execute(httpDelete);
            // 判断返回状态是否为200
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
//            System.out.println(resultString);
//            if (response.getStatusLine().getStatusCode() == 200) {
//                resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
//            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultString;
    }

    /**
     * POST请求
     *
     * @param url  请求URL
     * @param json 请求参数
     * @return String 请求返回
     */
    private String doPost(String url, String json) {

        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;

        String resultString = "";
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            System.out.println(url);

            // 创建请求内容
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);

            // 设置通用属性
            httpPost.setHeader("Accept", "*/*");
            httpPost.setHeader("Connection", "Keep-Alive");
            httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
            for (Object hd : this.headers.keySet()) {
                httpPost.setHeader(hd.toString(), this.headers.get(hd.toString()));
            }

            // 执行http请求
            response = httpClient.execute(httpPost);
            // 判断返回状态是否为200
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
//            System.out.println(resultString);
//            if (response.getStatusLine().getStatusCode() == 200) {
//                resultString = EntityUtils.toString(response.getEntity(), "utf-8");
//            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }

    /**
     * PUT请求
     *
     * @param url  请求URL
     * @param json 请求参数
     * @return String 请求返回
     */
    private String doPut(String url, String json) {

        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;

        String resultString = "";
        try {
            // 创建Http PUT请求
            HttpPut httpPut = new HttpPut(url);
            System.out.println(url);

            // 创建请求内容
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPut.setEntity(entity);

            // 设置通用属性
            httpPut.setHeader("Accept", "*/*");
            httpPut.setHeader("Connection", "Keep-Alive");
            httpPut.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
            for (Object hd : this.headers.keySet()) {
                httpPut.setHeader(hd.toString(), this.headers.get(hd.toString()));
            }

            // 执行http请求
            response = httpClient.execute(httpPut);
            // 判断返回状态是否为200
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
//            System.out.println(resultString);
//            if (response.getStatusLine().getStatusCode() == 200) {
//                resultString = EntityUtils.toString(response.getEntity(), "utf-8");
//            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }

    /**
     * 查询指定模型的所有数据
     *
     * @param objectId   模型ID
     * @param parameters 参数
     * @return List<Object>
     */
    public List<Object> sendPostWithAll(String objectId, Map<String, Object> parameters) {
        if (parameters == null) {
            parameters = new HashMap<String, Object>();
        }
        int page = 1;
        int page_size = 300;  // 默认覆盖300条数据一页
        parameters.put("page", page);
        parameters.put("page_size", page_size);

        List<Object> return_data = new ArrayList<Object>();
        while (true) {
            String all_instances_string = this.sendRequest("/cmdb_resource/object/" + objectId + "/instance/_search", parameters, "POST");
            Map all_instances_map = (Map) JSON.parse(all_instances_string);
            Map all_instances_data_map = (Map) all_instances_map.get("data");
            int ret_total = (Integer) all_instances_data_map.get("total");
            List ret_list = (List) all_instances_data_map.get("list");
            return_data.addAll(ret_list);

            if (ret_list.size() < (Integer) parameters.get("page_size")) {
                break;
            }
            if (return_data.size() < ret_total) {
                parameters.put("page", (Integer) parameters.get("page") + 1);
            } else {
                break;
            }
        }

        return return_data;
    }

    /**
     * 设置请求，所有OPENAPI从这里走请求
     *
     * @param uri        请求URI
     * @param parameters 请求参数
     * @param method     请求方法
     * @return ret
     */
    public String sendRequest(String uri, Map<String, Object> parameters, String method) {
        if (parameters == null) {
            parameters = new HashMap<String, Object>();
        }
        long request_time = System.currentTimeMillis() / 1000L;
        String signature = this.genSignature(uri, request_time, parameters, method);
        String request_url = "http://" + this.host + uri;
        String ret = "";

        if ("GET".equals(method) || "DELETE".equals(method)) {
            parameters.put("accesskey", this.ACCESS_KEY);
            parameters.put("signature", signature);
            parameters.put("expires", request_time + "");

            Map<String, String> mapNew = new HashMap<String, String>();
            for (String string : parameters.keySet()) {
                mapNew.put(string, parameters.get(string).toString());
            }
            if ("GET".equals(method)) {
                ret = doGet(request_url, mapNew);
            } else {
                ret = doDel(request_url, mapNew);
            }
        } else {
            request_url += "?accesskey=" + this.ACCESS_KEY + "&signature=" + signature + "&expires=" + request_time + "";
            if ("POST".equals(method)) {
                ret = doPost(request_url, JSONObject.toJSONString(parameters));
            } else {
                ret = doPut(request_url, JSONObject.toJSONString(parameters));
            }
        }
        return ret;
    }

    /***
     * 生成签名字段
     * @param uri 请求路径
     * @param request_time 请求时间戳
     * @param data 请求数据
     * @param method 请求方法
     * @return signature
     */
    private String genSignature(String uri, long request_time, Map<String, Object> data, String method) {

        String url_params = "";
        String str_sign = "";
        String signature = "";
        //body data md5sum
        String body_content = "";
        //GET做
        if (("GET".equals(method) || "DELETE".equals(method)) && !data.isEmpty()) {
            data = sortMapByKey(data);
            for (Object key : data.keySet()) {
                url_params = url_params + key + data.get(key.toString());
            }
        }

        //POST PUT做body数据的md5sum
        if (("POST".equals(method) || "PUT".equals(method)) && !data.isEmpty()) {
            JSONObject object = new JSONObject(data);
            String body = object.toJSONString();
            body_content = MD5(body, "UTF-8").toLowerCase();
        }

        str_sign = method + "\n" + uri + "\n" + url_params + "\n" +
                this.headers.get("Content-Type") + "\n" + body_content + "\n" +
                request_time + "\n" + this.ACCESS_KEY;
        System.out.println("************* SIGN *************");
        System.out.println(str_sign);
        System.out.println("********************************");
        signature = HmacSHA1Encrypt(str_sign, this.SECRET_KEY);
        return signature;
    }


    /**
     * 转MD5 防止中文乱码问题
     *
     * @param s            指定字符串
     * @param encodingType 指定编码
     * @return String
     */
    private static String MD5(String s, String encodingType) {
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

        try {
            // 按照相应编码格式获取byte[]
            byte[] btInput = s.getBytes(encodingType);
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(btInput);
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式

            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;

            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return "-1";
        }
    }


    /**
     * HASH算法加密
     *
     * @param encryptText 加密文本
     * @param encryptKey  加密算法
     * @return String
     */
    private static String HmacSHA1Encrypt(String encryptText, String encryptKey) {
        byte[] data = encryptKey.getBytes();
        SecretKey secretKey = new SecretKeySpec(data, "HmacSHA1");
        try {
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(secretKey);
            byte[] text = encryptText.getBytes();
            return Hex.encodeHexString(mac.doFinal(text));
        } catch (Exception ex) {
            return null;
        }
    }


    /**
     * 使用 Map按key进行排序
     *
     * @param map 排序MAP
     * @return sortMap
     */
    private static Map<String, Object> sortMapByKey(Map<String, Object> map) {
        if (map == null || map.isEmpty()) {
            return null;
        }

        Map<String, Object> sortMap = new TreeMap<String, Object>(new MapKeyComparator());

        sortMap.putAll(map);

        return sortMap;
    }


}


/**
 * MAP按照key排序的比较器
 */
class MapKeyComparator implements Comparator<String> {

    @Override
    public int compare(String str1, String str2) {
        return str1.compareTo(str2);
    }
}
